/* 
Scenic Graphics Library
Copyright (C) 2007 Jouni Tulkki

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USApackage scenic;
*/

#include "common.h"
#include "AAPolygonRenderer.h"


#ifdef DIRECTX
#define ALPHA 64
#endif
#ifdef OPT_OPENGL
#define ALPHA 255
#endif


#define ALPHA_COLOR(a) (((a) << 24) | ((a) << 16) | ((a) << 8) | (a))


using namespace Scenic::Renderer;


// VertexStore *AAPolygonRenderer::buffer = new VertexStore();
VertexStore *AAPolygonRenderer::vs = new VertexStore();
VertexStore *AAPolygonRenderer::subVs = new VertexStore();
VertexStore *AAPolygonRenderer::addVs = new VertexStore();


AAPolygonRenderer::AAPolygonRenderer(Context *context)
{
	this->context = context;

	vs->init(VSF_TEX1, context, 0, 0);
	addVs->init(VSF_TEX1, context, 0, 0);
	subVs->init(VSF_TEX1, context, 0, 0);
	/*
	vs = new VertexStore(VSF_TEX1, context, 0, 0);
	addVs = new VertexStore(VSF_TEX1, context, 0, 0);
	subVs = new VertexStore(VSF_TEX1, context, 0, 0);
	*/
	corner = 0;
	color0 = 0;
	color = ALPHA_COLOR(ALPHA);
	color2 = ALPHA_COLOR(ALPHA / 2);
	color8 = ALPHA_COLOR(ALPHA / 8);
	filterLength = 0.5;

	if(context->aaPolygonTexture == 0)
		createTexture();
}

AAPolygonRenderer::~AAPolygonRenderer()
{
/*	delete vs;
	delete addVs;
	delete subVs; */
}

void AAPolygonRenderer::begin()
{
	context->setPrimitives(0);

	context->device->setTexture(0, context->aaPolygonTexture);
	context->device->setSamplerStates(0, FILTER_LINEAR, ADDRESS_CLAMP);
	context->device->setTextureStageModulation(0, 1);
	context->device->setBlendFunc(BLEND_ONE, BLEND_ONE);
}

void AAPolygonRenderer::end()
{
	corner = 0;
	draw();
	context->device->setTexture(0, NULL);
}

void AAPolygonRenderer::addVertex(double x, double y)
{
	Double2 p;

	p.x = x * context->transform.m00 + y * context->transform.m01 + context->transform.dx;
	p.y = x * context->transform.m10 + y * context->transform.m11 + context->transform.dy;

	points.add(p);
//	buffer->addVertex(x, y, color, 1.0, 1.0);
}

void AAPolygonRenderer::addTriangle(int vertex1, bool edge1, int vertex2, bool edge2, int vertex3, bool edge3)
{
	TessVertex v1;
	TessVertex v2;
	TessVertex v3;

	v1.p[0] = points[vertex1].x;
	v1.p[1] = points[vertex1].y;

	v2.p[0] = points[vertex2].x;
	v2.p[1] = points[vertex2].y;

	v3.p[0] = points[vertex3].x;
	v3.p[1] = points[vertex3].y;

	triangle[0] = &v1;
	triangle[1] = &v2;
	triangle[2] = &v3;
	triangleEdge[0] = edge1;
	triangleEdge[1] = edge2;
	triangleEdge[2] = edge3;

	processTriangle();
}

void AAPolygonRenderer::draw()
{
	vs->draw();
	addVs->draw();
	context->device->setState(STATE_BLENDOP, BLENDOP_REVSUBTRACT);
	context->device->setState(STATE_BLENDOPALPHA, BLENDOP_REVSUBTRACT);
	subVs->draw();
	context->device->setState(STATE_BLENDOP, BLENDOP_ADD);
	context->device->setState(STATE_BLENDOPALPHA, BLENDOP_ADD);
}

void AAPolygonRenderer::processTriangle()
{
	int i;

	for(i = 0; i < 3; i++)
	{
		TessVertex *data = triangle[i];

		vs->addVertex(data->p[0], data->p[1], color, 1.0, 1.0);
	}
	vs->addTriangle(0, 1, 2);
	vs->endBlock();

	for(i = 0; i < 3; i++)
	{
		if(triangleEdge[i])
		{
			TessVertex *v1 = triangle[i];
			TessVertex *v2 = triangle[(i + 1) % 3];
	
			drawEdge2(v1, v2);
		}
	}
}

void AAPolygonRenderer::drawEdge(TessVertex *v1, TessVertex *v2)
{
	Double2 d;
	Double2 n;
	double m;
	double w = 1;

	n.x = v2->p[0] - v1->p[0];
	n.y = v2->p[1] - v1->p[1];

	m = w / sqrt(n.x * n.x + n.y * n.y);

	d.x = -n.y * m;
	d.y = n.x * m;

	vs->addVertex(v1->p[0] - d.x, v1->p[1] - d.y, color0);
	vs->addVertex(v2->p[0] - d.x, v2->p[1] - d.y, color0);
	vs->addVertex(v1->p[0], v1->p[1], color);
	vs->addVertex(v2->p[0], v2->p[1], color);

	vs->addTriangle(0, 1, 2);
	vs->addTriangle(2, 1, 3);
	vs->endBlock();
}

void AAPolygonRenderer::addCorner(Float2 p, double sign, double color)
{
	int addAlpha = (ALPHA / 8) + (int)((ALPHA / 8) * color);
	int addColor = ALPHA_COLOR(addAlpha);
	int subAlpha = (ALPHA / 8) - (int)((ALPHA / 8) * color);
	int subColor = ALPHA_COLOR(subAlpha);

	addVs->addVertex(p.x, p.y, addColor, 0.75, 0.75);
	addVs->addVertex(p.x - filterLength, p.y, addColor, 0.75, 0.25);
	addVs->addVertex(p.x, p.y - sign * filterLength, addColor, 0.25, 0.75);
	addVs->addVertex(p.x - filterLength, p.y - sign * filterLength, addColor, 0.25, 0.25);
	addVs->addVertex(p.x + filterLength, p.y, addColor, 0.75, 0.25);
	addVs->addVertex(p.x, p.y + sign * filterLength, addColor, 0.25, 0.75);
	addVs->addVertex(p.x + filterLength, p.y + sign * filterLength, addColor, 0.25, 0.25);

	addVs->addTriangle(0, 1, 2);
	addVs->addTriangle(1, 3, 2);
	addVs->addTriangle(0, 4, 5);
	addVs->addTriangle(4, 6, 5);
	addVs->endBlock();

	subVs->addVertex(p.x, p.y, subColor, 0.75, 0.75);
	subVs->addVertex(p.x - filterLength, p.y, subColor, 0.75, 0.25);
	subVs->addVertex(p.x, p.y + sign * filterLength, subColor, 0.25, 0.75);
	subVs->addVertex(p.x - filterLength, p.y + sign * filterLength, subColor, 0.25, 0.25);
	subVs->addVertex(p.x + filterLength, p.y, subColor, 0.75, 0.25);
	subVs->addVertex(p.x, p.y - sign * filterLength, subColor, 0.25, 0.75);
	subVs->addVertex(p.x + filterLength, p.y - sign * filterLength, subColor, 0.25, 0.25);

	subVs->addTriangle(0, 1, 2);
	subVs->addTriangle(1, 3, 2);
	subVs->addTriangle(0, 4, 5);
	subVs->addTriangle(4, 6, 5);
	subVs->endBlock();
}

void AAPolygonRenderer::drawEdge2(TessVertex *v1, TessVertex *v2)
{
	Double2 d;
	Double2 n;

	n.x = v2->p[0] - v1->p[0];
	n.y = v2->p[1] - v1->p[1];

	d.x = 0;
	d.y = 0;

	if(fabs(n.x) > fabs(n.y))
		d.y = n.x > 0.0 ? filterLength : -filterLength;
	else
		d.x = n.y < 0.0 ? filterLength : -filterLength;

	addVs->addVertex(v1->p[0] - d.x, v1->p[1] - d.y, color0, 1.0, 1.0);
	addVs->addVertex(v2->p[0] - d.x, v2->p[1] - d.y, color0, 1.0, 1.0);
	addVs->addVertex(v1->p[0], v1->p[1], color2, 1.0, 1.0);
	addVs->addVertex(v2->p[0], v2->p[1], color2, 1.0, 1.0);

	addVs->addTriangle(0, 1, 2);
	addVs->addTriangle(2, 1, 3);
	addVs->endBlock();

	subVs->addVertex(v1->p[0] + d.x, v1->p[1] + d.y, color0, 1.0, 1.0);
	subVs->addVertex(v2->p[0] + d.x, v2->p[1] + d.y, color0, 1.0, 1.0);
	subVs->addVertex(v1->p[0], v1->p[1], color2, 1.0, 1.0);
	subVs->addVertex(v2->p[0], v2->p[1], color2, 1.0, 1.0);

	subVs->addTriangle(0, 1, 2);
	subVs->addTriangle(2, 1, 3);
	subVs->endBlock();

	Float2 p1 = {v1->p[0], v1->p[1]};
	Float2 p2 = {v2->p[0], v2->p[1]};

	if(d.y == 0.0) 
	{
		if(n.y == 0.0)
			return;
		addCorner(p1, -1.0, n.x / n.y);
		addCorner(p2, 1.0, -n.x / n.y);
	}
	else 
	{
		if(n.x == 0.0)
			return;
		addCorner(p1, 1.0, -n.y / n.x);
		addCorner(p2, -1.0, n.y / n.x);
	}
}

void AAPolygonRenderer::createTexture()
{
	context->aaPolygonTexture = new Texture(2, 2, FMT_A8R8G8B8, 0);
	int dest[4];

	dest[0] = 0x00000000;
	dest[1] = 0x00000000;
	dest[2 + 0] = 0x00000000;
	dest[2 + 1] = 0xffffffff;

	context->aaPolygonTexture->write(0, 0, 0, 2, 2, dest, 0, FMT_A8R8G8B8);
}
